package org.light.reflecttable.utils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import org.apache.logging.log4j.util.Strings;
import org.light.domain.Domain;
import org.light.domain.Dropdown;
import org.light.domain.Field;
import org.light.domain.FieldSerialComparator;
import org.light.domain.ManyToMany;
import org.light.domain.ManyToManyCandidate;
import org.light.domain.Project;
import org.light.exception.ValidateException;
import org.light.facade.ExcelWizardFacade;
import org.light.reflecttable.domain.Table;
import org.light.utils.DomainTokenUtil;
import org.light.utils.DomainUtil;
import org.light.utils.FieldUtil;
import org.light.utils.PluralUtil;
import org.light.utils.StringUtil;

public class ReflectUtil {
	public static List<String> collectTablePrefix(List<Table> tables){
		List<String> tableNames = new ArrayList<>();
		Map<String, Integer> prefixes = new TreeMap<>();
		for (Table t: tables) {
			tableNames.add(t.getTableName());
		}
		for (String tname:tableNames) {
			if (!tname.contains("_")) return new ArrayList<>();
			String prefix = tname.substring(0,tname.indexOf("_")+1);
			Integer count = 0;
			if (prefixes.get(prefix) != null) {
				count = prefixes.get(prefix);
			}
			prefixes.put(prefix, ++count);
		}
		if (prefixes.size()>0&&prefixes.size() <= 3) {
			List<String> results = new ArrayList<>();
			Integer max = 0;
			String maxkey = "";
			for (String key: prefixes.keySet()) {
				if (prefixes.get(key) > max) {
					max = prefixes.get(key);
					maxkey = key;
				}
			}
			results.add(maxkey);
			for (String key: prefixes.keySet()) {
				if (!key.equals(maxkey)) {
					results.add(key);
				}
			}
			if (max >= 2) return results;
			else return new ArrayList<>();
		}
		else return new ArrayList<>();
	}
	
	public static List<String> collectTablePrefixFromRawDomain(List<Domain> domains) throws ValidateException{
		List<String> tableNames = new ArrayList<>();
		Map<String, Integer> prefixes = new TreeMap<>();
		for (Domain td: domains) {
			tableNames.add(td.getPlural().toLowerCase());
		}
		for (String tname:tableNames) {
			if (!tname.contains("_")) return new ArrayList<>();
			String prefix = tname.substring(0,tname.indexOf("_")+1);
			Integer count = 0;
			if (prefixes.get(prefix) != null) {
				count = prefixes.get(prefix);
			}
			prefixes.put(prefix, ++count);
		}
		if (prefixes.size()>0&&prefixes.size() <= 3) {
			List<String> results = new ArrayList<>();
			Integer max = 0;
			String maxkey = "";
			for (String key: prefixes.keySet()) {
				if (prefixes.get(key) > max) {
					max = prefixes.get(key);
					maxkey = key;
				}
			}
			results.add(maxkey);
			for (String key: prefixes.keySet()) {
				if (!key.equals(maxkey)) {
					results.add(key);
				}
			}
			return results;
		}
		else return new ArrayList<>();
	}
	
	public static List<Domain> genDomainNamesFromTables(List<Table> tables) throws ValidateException{
		List<Domain> results = new ArrayList<>();
		List<String> tablePrefixes = collectTablePrefix(tables);
		if (tablePrefixes.size() == 0) {
			for (Table t: tables) {
				Domain d = new Domain();
				d.setPlural(org.light.utils.StringUtil.changeTableColumtoDomainField(t.getTableName()));
				d.setStandardName(PluralUtil.lookupSingle(org.light.utils.StringUtil.changeTableColumtoDomainField(t.getTableName())));
				results.add(d);
			}
		} else {
			String mainPrefix = tablePrefixes.get(0);
			for (Table t: tables) {
				Domain d = new Domain();
				d.setDbPrefix(mainPrefix);
				String tname = t.getTableName();
				if (!tname.startsWith(mainPrefix)) {
					for (String pre:tablePrefixes) {
						if (tname.startsWith(pre)) {
							d.setTablePrefix(pre);
							String shortName = tname.substring(pre.length()-1,tname.length());
							d.setPlural(org.light.utils.StringUtil.changeTableColumtoDomainField(shortName));
							d.setStandardName(PluralUtil.lookupSingle(org.light.utils.StringUtil.changeTableColumtoDomainField(shortName)));
						}
					}
				} else {
					String shortName = tname.substring(mainPrefix.length()-1,tname.length());
					d.setPlural(org.light.utils.StringUtil.changeTableColumtoDomainField(shortName));
					d.setStandardName(PluralUtil.lookupSingle(org.light.utils.StringUtil.changeTableColumtoDomainField(shortName)));
				}
				results.add(d);
			}
		}
		return results;
	}

	public static List<Domain> decorateDomainNamesFromRawDomains(List<Domain> rawDomains){
		try {
		List<Domain> results = new ArrayList<>();
		List<String> tablePrefixes = collectTablePrefixFromRawDomain(rawDomains);
		if (tablePrefixes.size() == 0) {
			for (Domain td: rawDomains) {
				String tname = org.light.utils.StringUtil.changeTableColumtoDomainField(td.getPlural().toLowerCase());
				Map<String,String> lookup = PluralUtil.lookupSingleAndPlural(tname);
				td.setPlural(lookup.get("plural"));
				td.setStandardName(lookup.get("single"));
				results.add(td);
			}
		} else {
			String mainPrefix = tablePrefixes.get(0);
			for (Domain td: rawDomains) {
				td.setDbPrefix(mainPrefix);
				String rtname = td.getPlural().toLowerCase();
				if (!rtname.startsWith(mainPrefix)) {
					for (String pre:tablePrefixes) {
						if (rtname.startsWith(pre)) {
							td.setTablePrefix(pre);
							td.setDbPrefix(mainPrefix);
							String rshortName = rtname.substring(pre.length(),rtname.length());
							String shortName = org.light.utils.StringUtil.changeTableColumtoDomainField(rshortName);
							Map<String,String> lookup = PluralUtil.lookupSingleAndPlural(shortName);
							td.setPlural(lookup.get("plural"));
							td.setStandardName(lookup.get("single"));
						}
					}
				} else {
					String rshortName = rtname.substring(mainPrefix.length(),rtname.length());
					String shortName = org.light.utils.StringUtil.changeTableColumtoDomainField(rshortName);
					td.setDbPrefix(mainPrefix);
					Map<String,String> lookup = PluralUtil.lookupSingleAndPlural(shortName);
					td.setPlural(lookup.get("plural"));
					td.setStandardName(lookup.get("single"));
				}
				results.add(td);
			}
		}
		for (Domain rstd: results) {
			System.out.println("JerryDebug:prefix:"+rstd.getPlural()+":"+rstd.getStandardName());
		}
		return results;
		} catch (ValidateException e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
			return null;
		}
	}
	
	public static String lookupRustTypeByMySqlType(String sqlType){
		String result = "String";
		if (sqlType.toLowerCase().startsWith("bigint")) {
			result = "i64";
		} else if  (sqlType.toLowerCase().startsWith("int")) {
			result = "i32";
		} else if (sqlType.toLowerCase().startsWith("float")) {
			result = "f32";
		} else if (sqlType.toLowerCase().startsWith("double")) {
			result = "f64";
		} else if (sqlType.toLowerCase().startsWith("real")) {
			result = "f64";
		} else if (sqlType.toLowerCase().startsWith("decimal")) {
			result = "f64";
		} else if (sqlType.equalsIgnoreCase("bigint(20)")) {
			result = "i64";
		} else if (sqlType.equalsIgnoreCase("int(11)")) {
			result = "i32";
		} else if (sqlType.equalsIgnoreCase("tinyint(1)")) {
			result = "bool";
		} else if (sqlType.startsWith("tinyint")) {
			result = "bool";
		} else if (sqlType.equalsIgnoreCase("longblob")) {
			result = "image";
		} else if (sqlType.equalsIgnoreCase("datetime")) {
			result = "datetime";
		} else if (sqlType.equalsIgnoreCase("date")) {
			result = "date";
		} else if (sqlType.toLowerCase().contains("char")||sqlType.toLowerCase().contains("text")) {
			result = "String";
		}
		return result; 
	}
	
	public static List<Domain> decorateDomainMetaFieldsFromRawDomains(List<Domain> rawDomains, String dbType){
		List<Domain> results = new ArrayList<>();
		for (Domain rd:rawDomains) {
			Domain d = decorateDomainMetaFieldsFromRawDomain(rd, dbType);
			results.add(d);
		}
		return results;
	}
	
	public static Domain decorateDomainMetaFieldsFromRawDomain(Domain rawDomain, String dbType){
		Field idf = null;
		if ("MariaDB".equalsIgnoreCase(dbType)|| "MySQL".equalsIgnoreCase(dbType)) idf = decideDomainIdField(rawDomain);
		else if ("PostgreSQL".equalsIgnoreCase(dbType) || "pgsql".equalsIgnoreCase(dbType)) idf = decidePgDomainIdField(rawDomain);
		Field actf = decideActiveField(rawDomain);
		Field nmf = decideDomainNameField(rawDomain);
		Set<Field> denies = new TreeSet<>(new FieldSerialComparator());
		if(idf != null) idf.setSerial(-300);
		if(nmf != null) nmf.setSerial(-200);
		if(actf != null) actf.setSerial(-100);
		if (idf!=null) denies.add(idf);
		if (actf!=null) denies.add(actf);
		if (nmf!=null) denies.add(nmf);
		Set<Field> plainFieldsSet = FieldUtil.filterDeniedFields(rawDomain.getFields(), denies);
		rawDomain.setDomainId(idf);
		rawDomain.setActive(actf);
		rawDomain.setDomainName(nmf);
		rawDomain.setFields(plainFieldsSet);
		return rawDomain;
	}

	public static Field decideActiveField(Domain d) {
		List<Field> canidates = filterFieldsByTypeName(d, "bool");
		if (canidates.size() == 1) return canidates.get(0);
		for (Field f : canidates) {
			if (f.getFieldName().equalsIgnoreCase("active")) return f;
		}
		for (Field f : canidates) {
			if (f.getFieldName().equalsIgnoreCase("deleted")) return f;
		}
		for (Field f : canidates) {
			if (f.getFieldName().equalsIgnoreCase("delete")) return f;
		}
		for (Field f : canidates) {
			if (f.getFieldName().toLowerCase().contains("active")) return f;
		}
		for (Field f : canidates) {
			if (f.getFieldName().toLowerCase().contains("delete")) return f;
		}
		return null;
	}
	
	public static Field decideDomainIdField(Domain d) {
		List<Field> canidates = filterFieldsByTypeName(d, "i64");
		for (Field f : canidates) {
			if (f.getFieldName().equalsIgnoreCase("id")&&f.getFieldNull()!=null&&f.getFieldNull().equalsIgnoreCase("NO")&&f.getFieldKey()!=null&&f.getFieldKey().equalsIgnoreCase("PRI")&&f.getFieldExtra()!=null&&f.getFieldExtra().equalsIgnoreCase("auto_increment")) return f;
		}
		for (Field f : canidates) {
			if (f.getFieldName().equalsIgnoreCase("id")&&f.getFieldNull()!=null&&f.getFieldNull().equalsIgnoreCase("NO")&&f.getFieldKey()!=null&&f.getFieldKey().equalsIgnoreCase("PRI")) return f;
		}
		for (Field f : canidates) {
			if (f.getFieldName().equalsIgnoreCase(d.getStandardName()+"id")&&f.getFieldNull()!=null&&f.getFieldNull().equalsIgnoreCase("NO")&&f.getFieldKey()!=null&&f.getFieldKey().equalsIgnoreCase("PRI")&&f.getFieldExtra()!=null&&f.getFieldExtra().equalsIgnoreCase("auto_increment")) return f;
		}
		for (Field f : canidates) {
			if (f.getFieldKey()!=null&&f.getFieldKey().equalsIgnoreCase("PRI")&&f.getFieldNull()!=null&&f.getFieldNull().equalsIgnoreCase("NO")&&f.getFieldExtra()!=null&&f.getFieldExtra().equalsIgnoreCase("auto_increment")) return f;
		}
		for (Field f : canidates) {
			if (f.getFieldNull()!=null&&f.getFieldNull().equalsIgnoreCase("NO")&&f.getFieldKey()!=null&&f.getFieldKey().equalsIgnoreCase("PRI")) return f;
		}
		return null;
	}
	
	public static Field decidePgDomainIdField(Domain d) {
		List<Field> canidates = filterFieldsByTypeName(d, "i64");
		for (Field f : canidates) {
			if (f.getFieldName().equalsIgnoreCase("id")&&f.getFieldNull()!=null&&f.getFieldNull().trim().toLowerCase().contains("no")&&f.getFieldDefault()!=null&&f.getFieldDefault().toLowerCase().contains("nextval")) return f;
		}
		for (Field f : canidates) {
			if (f.getFieldName().equalsIgnoreCase(d.getStandardName()+"Id")&&f.getFieldNull()!=null&&f.getFieldNull().trim().toLowerCase().contains("no")&&f.getFieldDefault()!=null&&f.getFieldDefault().toLowerCase().contains("nextval")) return f;
		}
		for (Field f : canidates) {
			if (f.getFieldNull()!=null&&f.getFieldNull().trim().toLowerCase().contains("no")&&f.getFieldDefault()!=null&&f.getFieldDefault().toLowerCase().contains("nextval")) return f;
		}
		return null;
	}
	
	public static Field decideDomainNameField(Domain d) {
		List<Field> canidates = filterFieldsByTypeName(d, "String");
		for (Field f : canidates) {
			if (f.getFieldName().equalsIgnoreCase(d.getStandardName()+"Name")&&f.getFieldNull()!=null&&f.getFieldNull().equalsIgnoreCase("NO")) return f;
		}
		for (Field f : canidates) {
			if (f.getFieldName().toLowerCase().contains("name")&&f.getFieldNull()!=null&&f.getFieldNull().equalsIgnoreCase("NO")) return f;
		}
		if (canidates.size() == 1 &&canidates.get(0).getFieldNull()!=null && canidates.get(0).getFieldNull().equalsIgnoreCase("NO")) return canidates.get(0);
		return null;
	}
	
	public static List<Field> filterFieldsByTypeName(Domain domain, String typeName){
		List<Field> result = new ArrayList<>();
		for (Field f: domain.getFields()) {
			if (typeName.equalsIgnoreCase(f.getFieldType())) {
				result.add(f);
			}
		}
		return result;
	}
	
	public static List<Field> filterFieldsByTypeNameHead(Domain domain, String typeName){
		List<Field> result = new ArrayList<>();
		for (Field f: domain.getFields()) {
			if (typeName.startsWith(f.getFieldType())) {
				result.add(f);
			}
		}
		return result;
	}
	
	public static Map<String,Object> decideManyToMany(Domain master,List<Domain> domains) throws Exception{
		Map<String,Object> result = new TreeMap<>();
		List<ManyToMany> manyToManies = new ArrayList<>();
		List<List<ManyToMany>> vmtmls = new ArrayList<>();
		List<String> removeDomainNames = new ArrayList<>();
		String masterName = "";
		String slaveName = "";
		for (Domain d:domains) {
			Set<Field> fs = d.getFields();
			if (fs.size() != 2) continue;
			for (Field f : fs) {
				if (!f.getFieldType().equalsIgnoreCase("i64")) continue;
			}
			for (Field f : fs) {
				if (!f.getFieldName().endsWith("Id")) continue;
			}
			for (Field f : fs) {
				if (f.getFieldName().equals(master.getLowerFirstDomainName()+"Id")||f.getFieldName().equals(master.getLowerFirstPlural()+"Id")) {
					Field masterId = f;
					Field slaveId = null;
					for (Field f2 :fs) {
						if (!f2.getFieldName().equals(masterId.getFieldName())) slaveId = f2;
					}
					if (masterId != null && slaveId != null) {
						Map<String,Object> inresult = findManyToManyInDomains(masterId,slaveId,domains);
						if (inresult != null && inresult.size()>0) {
							Domain master0 = (Domain)inresult.get("master");
							Domain slave0 = (Domain)inresult.get("slave");
							ManyToMany mtm = null;
							List<ManyToMany> vmtms = new ArrayList<>();
							if (d.getPlural().startsWith(master0.getStandardName())) {
								mtm = new ManyToMany(master0,slave0,"",slave0.getStandardName());
							} else if (d.getPlural().startsWith(slave0.getStandardName())) {
								mtm = new ManyToMany(slave0,master0,"",master0.getStandardName());
							} else if (d.getPlural().startsWith(master0.getPlural())) {
								mtm = new ManyToMany(master0,slave0,"",slave0.getStandardName());
							} else if (d.getPlural().startsWith(slave0.getPlural())) {
								mtm = new ManyToMany(slave0,master0,"",master0.getStandardName());
							}
							for (Domain dd:d.getDummyDb()){
								ManyToMany vm = (ManyToMany)mtm.deepClone();
								vm.setMasterValue(dd.findFieldByFieldName(mtm.getMaster().getLowerFirstDomainName()+"Id").getFieldValue());
								vm.setValues(dd.findFieldByFieldName(mtm.getSlave().getLowerFirstDomainName()+"Id").getFieldValue());
								vmtms.add(vm);
							}
							manyToManies.add(mtm);
							vmtmls.add(vmtms);
							removeDomainNames.add(d.getStandardName());
							masterName = master0.getStandardName();
							slaveName = slave0.getStandardName();
						}
					}
				}
			}
		}
		if (manyToManies.size()>0) {
			result.put("manyToManies", manyToManies);
			result.put("mtmValues", vmtmls);
			result.put("removeDomainNames", removeDomainNames);
			result.put("masterName", masterName);
			result.put("slaveName", slaveName);
			return result;
		}
		return null;
	}
	
	public static Map<String,Object> decideManyToManyCandidate(Domain master,List<Domain> domains) throws Exception{
		Map<String,Object> result = new TreeMap<>();
		List<ManyToManyCandidate> mtmcs = new ArrayList<>();
		List<String> removeDomainNames = new ArrayList<>();
		for (Domain d:domains) {
			Set<Field> fs = d.getFields();
			if (fs.size() != 2) continue;
			for (Field f : fs) {
				if (!f.getFieldType().equalsIgnoreCase("i64") && !(f instanceof Dropdown)) continue;
			}
			for (Field f : fs) {
				if (!f.getFieldName().endsWith("Id")) continue;
			}
			for (Field f : fs) {
				if (f.getFieldName().equals(master.getLowerFirstDomainName()+"Id")||f.getFieldName().equals(master.getLowerFirstPlural()+"Id")) {
					Field masterId = f;
					Field slaveId = null;
					for (Field f2 :fs) {
						if (!f2.getFieldName().equals(masterId.getFieldName())) slaveId = f2;
					}
					if (masterId != null && slaveId != null) {
						ManyToManyCandidate mtmc = new ManyToManyCandidate();
						mtmc.setMaster(master);
						mtmc.setMasterId(masterId);
						mtmc.setSlaveId(slaveId);
						mtmc.setMasterName(master.getStandardName());
						mtmc.setSlaveAlias(slaveId.getCapFirstFieldName().substring(0,slaveId.getFieldName().length()-2));
						mtmc.setMasterIdValue(masterId.getFieldValue());
						mtmc.setSlaveIdValues(slaveId.getFieldValue());
						String mtmcLabel = d.getLabel();
						if (mtmcLabel.startsWith("链接")) mtmcLabel = mtmcLabel.substring("链接".length(), mtmcLabel.length());
						else if (mtmcLabel.startsWith("Link")) mtmcLabel = mtmcLabel.substring("Link".length(), mtmcLabel.length());
						if (mtmcLabel.startsWith(master.getText())) mtmcLabel = mtmcLabel.substring(master.getText().length(),mtmcLabel.length());
						mtmc.setLabel(mtmcLabel);
						mtmcs.add(mtmc);
						removeDomainNames.add(d.getStandardName());;							
					}
				}
			}
		}
		if (mtmcs.size()>0) {
			result.put("mtmCandidates", mtmcs);
			result.put("removeDomainNames", removeDomainNames);
			return result;
		}
		return null;
	}
	
	public static ManyToManyCandidate domainToManyToManyCandidate(Domain d, Domain master,List<Domain> domains) throws Exception{
		Set<Field> fs = d.getFields();
		if (fs.size() != 2) return null;
		for (Field f : fs) {
			if (!f.getFieldType().equalsIgnoreCase("i64") && !(f instanceof Dropdown)) continue;
		}
		for (Field f : fs) {
			if (!f.getFieldName().endsWith("Id")) continue;
		}
		for (Field f : fs) {
			if (f.getFieldName().equals(master.getLowerFirstDomainName()+"Id")||f.getFieldName().equals(master.getLowerFirstPlural()+"Id")) {
				Field masterId = f;
				Field slaveId = null;
				for (Field f2 :fs) {
					if (!f2.getFieldName().equals(masterId.getFieldName())) slaveId = f2;
				}
				if (masterId != null && slaveId != null) {
					ManyToManyCandidate mtmc = new ManyToManyCandidate();
					mtmc.setMaster(master);
					mtmc.setMasterId(masterId);
					mtmc.setSlaveId(slaveId);
					mtmc.setSlaveAlias(slaveId.getCapFirstFieldName().substring(0,slaveId.getFieldName().length()-2));
					mtmc.setMasterIdValue(masterId.getFieldValue());
					mtmc.setSlaveIdValues(slaveId.getFieldValue());
					mtmc.setLabel(d.getLabel());
					return mtmc;
				}
			}
		}
		return null;
	}
	
	public static Map<String,Object> findManyToManyInDomains(Field masterId,Field slaveId, List<Domain> domains) throws Exception{
		Domain master = null;
		Domain slave = null;
		Map<String, Object> result = new TreeMap<>();
		for (Domain d: domains) {
			if (d.getDomainId()==null) continue;
			if (masterId.getFieldName().equals(d.getLowerFirstPlural()+"Id")) master = d; 
			if (slaveId.getFieldName().equals(d.getLowerFirstPlural()+"Id")) slave = d; 
		}
		
		for (Domain d: domains) {
			if (d.getDomainId()==null) continue;
			if (masterId.getFieldName().equals(d.getLowerFirstDomainName()+"Id")) master = d; 
			if (slaveId.getFieldName().equals(d.getLowerFirstDomainName()+"Id")) slave = d; 
		}

		if (master != null && slave != null) {
			System.out.println("JerryDebug:Mtms:"+master.getStandardName()+":"+slave.getStandardName());
			result.put("master", master);
			result.put("slave", slave);
			return result;
		}
		return null;
	}
	
	public static List<Domain> decorateManyToManiesFromRawDomains(List<Domain> rawDomains) throws Exception{
		List<String> removeNames = new ArrayList<>();
		for (Domain rd:rawDomains) {
			Map findResults = decideManyToMany(rd,rawDomains);
			if (findResults != null && findResults.size()>0 && rd.getStandardName().equals((String)findResults.get("masterName"))) {
				List<ManyToMany> mtms = (List<ManyToMany>) findResults.get("manyToManies");
				List<List<ManyToMany>> vmtmls = (List<List<ManyToMany>>) findResults.get("mtmValues");
				List<String> removeDomainNames = (List<String>) findResults.get("removeDomainNames");
				removeNames.addAll(removeDomainNames);
				for (int j=0;j<mtms.size();j++) {
					ManyToMany mtm = mtms.get(j);
					if (rd.getStandardName().equals(mtm.getMaster().getStandardName())) {
						mtm.setStandardName(mtm.getSlave().getStandardName());
						mtm.setManyToManySalveName(mtm.getSlave().getStandardName());
						rd.addManyToMany(mtm);
					}
					List<ManyToMany> vmtms = vmtmls.get(j);
					rd = decorateDataManyToManies(rd,vmtms);
				}
			}
		}
		for (int i=0;i<rawDomains.size();i++) {
			if (removeNames.contains(rawDomains.get(i).getStandardName())) rawDomains.remove(i--);
		}
		return rawDomains;
	}
	
	private static Domain decorateDataManyToManies(Domain rawDomain,List<ManyToMany> vmtms) throws ValidateException{
		for (ManyToMany mtm:rawDomain.getManyToManies()) {
			if (rawDomain.getStandardName().equals(mtm.getMaster().getStandardName())) {
				for (int k=0;k<rawDomain.getDummyDb().size();k++) {
					Domain dd = rawDomain.getDummyDb().get(k);
					dd = ReflectUtil.decorateEmptyMtmToDataDomain(rawDomain, dd);
					dd = ReflectUtil.attachMtmToDataDomain(rawDomain,dd,mtm,vmtms);
				}
			}
		}
		return rawDomain;
	}
	
	public static String lookupRustTypeByPostgreSqlType(String sqlType){
		String result = "String";
		if (sqlType.equalsIgnoreCase("bigint")) {
			result = "i64";
		} else if (sqlType.equalsIgnoreCase("integer")) {
			result = "i32";
		} else if (sqlType.equalsIgnoreCase("serial")) {
			result = "i32";
		} else if (sqlType.equalsIgnoreCase("smallint")) {
			result = "i32";
		} else if (sqlType.equalsIgnoreCase("smallserial")) {
			result = "i32";
		} else if (sqlType.equalsIgnoreCase("bigserial")) {
			result = "i64";
		} else if (sqlType.equalsIgnoreCase("boolean")) {
			result = "bool";
		} else if (sqlType.toLowerCase().contains("double")) {
			result = "f64";
		} else if (sqlType.toLowerCase().contains("real")) {
			result = "f32";
		} else if (sqlType.toLowerCase().contains("decimal")) {
			result = "f64";
		} else if (sqlType.toLowerCase().contains("numeric")) {
			result = "f64";
		} else if (sqlType.equalsIgnoreCase("bytea")) {
			result = "image";
		} else if (sqlType.toLowerCase().contains("timestamp")) {
			result = "datetime";
		} else if (sqlType.equalsIgnoreCase("date")) {
			result = "date";
		} else if (sqlType.toLowerCase().contains("character")||sqlType.toLowerCase().contains("text")) {
			result = "String";
		}
		return result; 
	}
	
	public static String generateSelectAllStatement(Domain domain, String tableName, int topn) throws Exception{
		String result = "";
		if ("mariadb".equalsIgnoreCase(domain.getDbType()) ||"mysql".equalsIgnoreCase(domain.getDbType())){
			result = "select "+ generateTableCommaFieldsWithQuotes(domain) + " from "+ tableName;
		} else {
			result = "select "+ DomainTokenUtil.generateTableCommaFields(domain) + " from "+ tableName;
		}
		if (topn < 0) {
			return result;
		} else if (topn >= 0 && ("mariadb".equalsIgnoreCase(domain.getDbType()) ||"mysql".equalsIgnoreCase(domain.getDbType()))) {
			return result + " limit "+ topn;
		} else if (topn > 0 && ("postgresql".equalsIgnoreCase(domain.getDbType()) ||"pgsql".equalsIgnoreCase(domain.getDbType()))) {
			return result + " limit "+ topn;
		} else {
			return result;
		}
	}

	public static String generateTableCommaFieldsWithQuotes(Domain domain){
		Set<Field> set = domain.getFields();
		StringBuilder sb = new StringBuilder();
		for (Field f: set){
			sb.append("`").append(DomainTokenUtil.changeDomainFieldtoTableColum(f.getFieldName())).append("`,");
		}
		if (set.size() > 0) sb.deleteCharAt(sb.length()-1);
		return sb.toString();
	}

	public static List<Domain> decorateDropdownsFromRawDomains(List<Domain> domainList) throws Exception{
		for (Domain d : domainList) {
			decorateDomainDropdownsFromRawDomains(d, domainList);
			for (Domain dd:d.getDummyDb()){
				decorateDomainDropdownsFromRawDomains(dd, domainList);
			}
		}
		return domainList;
	}
	
	public static void decorateDomainDropdownsFromRawDomains(Domain targetDomain, List<Domain> domainList) throws Exception{
		for (Field f: targetDomain.getPlainFields()) {
			if (f.getFieldType().equals("i64") && f.getFieldName().endsWith("Id")) {
				Domain d = null;
				if ("parentId".equals(f.getFieldName())) {
					d = targetDomain;
				} else {
					String shortName = org.light.utils.StringUtil.capFirst(f.getFieldName().substring(0, f.getFieldName().length() - 2));
					d = DomainUtil.findDomainInListOrReturnNull(domainList, shortName);
				}
				if (d != null) {
					Dropdown dp = new Dropdown();
					dp.setSerial(f.getSerial());
					dp.setFieldType(d.getStandardName());
					dp.setTargetName(d.getStandardName());
					dp.setFieldName(f.getFieldName());
					dp.setLabel(f.getLabel());
					dp.setFixedName(f.getFixedName());
					dp.setFieldValue(f.getFieldValue());
					targetDomain.removeField(f.getFieldName(), "field");
					targetDomain.pureAddPlainField(dp);
				}
			}
		}
	}
	public static List<ManyToManyCandidate> decorateMtmCandidatesFromRawDomains(List<Domain> domains) throws Exception{
		List<ManyToManyCandidate> mtmcsl = new ArrayList<>();
		for (Domain rd:domains) {
			Map findResults = decideManyToManyCandidate(rd,domains);
			if (findResults != null && findResults.size()>0) {
				List<ManyToManyCandidate> mtmcs = (List<ManyToManyCandidate>) findResults.get("mtmCandidates");
				List<String> removeDomainNames = (List<String>) findResults.get("removeDomainNames");
				mtmcsl.addAll(mtmcs);
			}
		}
		return mtmcsl;
	}

	public static List<Domain> filterMtmCandidatesFromRawDomains(List<Domain> domains) throws Exception{
		List<String> removeNames = new ArrayList<>();
		for (Domain rd:domains) {
			Map findResults = decideManyToManyCandidate(rd,domains);
			if (findResults != null && findResults.size()>0) {
				List<ManyToManyCandidate> mtms = (List<ManyToManyCandidate>) findResults.get("mtmCandidates");
				List<String> removeDomainNames = (List<String>) findResults.get("removeDomainNames");
				removeNames.addAll(removeDomainNames);
			}
		}
		for (int i=0;i<domains.size();i++) {
			if (removeNames.contains(domains.get(i).getStandardName())) domains.remove(i--);
		}
		return domains;
	}
	
	public static List<List<ManyToManyCandidate>> getMtmCandidateValuesFromRawDomains(List<Domain> domains) throws Exception{
		List<ManyToManyCandidate> mtmcsl = new ArrayList<>();
		List<String> removeNames = new ArrayList<>();
		List<List<ManyToManyCandidate>> result = new ArrayList<>();
		for (Domain rd:domains) {
			Map findResults = decideManyToManyCandidate(rd,domains);
			if (findResults != null && findResults.size()>0) {
				List<ManyToManyCandidate> mtmcs = (List<ManyToManyCandidate>) findResults.get("mtmCandidates");
				List<String> removeDomainNames = (List<String>) findResults.get("removeDomainNames");
				mtmcsl.addAll(mtmcs);
				removeNames.addAll(removeDomainNames);
			}
		}
		for (int i=0; i< domains.size();i++) {
			if (removeNames.contains(domains.get(i).getStandardName())) {
				List<Domain> dataDomains = domains.get(i).getDummyDb();
				List<ManyToManyCandidate> mtmcs = new ArrayList<>();
				String removeName = domains.get(i).getStandardName();
				int rPos = removeNames.indexOf(removeName);
				for (Domain d: dataDomains) {
					ManyToManyCandidate mtmc = (ManyToManyCandidate) mtmcsl.get(rPos).deepClone();
					mtmc.setMasterIdValue(d.findFieldByFieldName(mtmc.getMasterId().getFieldName()).getFieldValue());
					mtmc.setSlaveIdValues(d.findFieldByFieldName(mtmc.getSlaveId().getFieldName()).getFieldValue());
					mtmcs.add(mtmc);
				}
				result.add(mtmcs);
			}
		}
		return result;
	}
	
	public static void decorateJEEEUField(Field field){
		if ("i32".equals(field.getFieldType())) field.setFieldType("Integer"); 
		if ("i64".equals(field.getFieldType())) field.setFieldType("Long");
		if ("f32".equals(field.getFieldType())) field.setFieldType("Float");	
		if ("f64".equals(field.getFieldType())) field.setFieldType("Double");	
		if ("bool".equals(field.getFieldType())) field.setFieldType("Boolean");		
	}
	
	public static void decorateJEEEUDomain(Domain domain){
		for (Field f: domain.getFields()) {
			if (! (f instanceof Dropdown)) {
				decorateJEEEUField(f);
			}
		}		
	}

	public static void decorateSimpleField(Field field){
		if ("i32".equals(field.getFieldType())) field.setFieldType("int32"); 
		if ("i64".equals(field.getFieldType())) field.setFieldType("int64");
		if ("f32".equals(field.getFieldType())) field.setFieldType("float32");	
		if ("f64".equals(field.getFieldType())) field.setFieldType("float64");	
		if ("String".equals(field.getFieldType())) field.setFieldType("string");		
	}
	
	public static void decorateSimpleDomain(Domain domain){
		for (Field f: domain.getFields()) {
			if (! (f instanceof Dropdown)) {
				decorateSimpleField(f);
			}
		}		
	}
	
	public static void decorateJEEEUMtmCandidate(ManyToManyCandidate mtmc){
		if (mtmc.getMaster() != null) decorateJEEEUDomain(mtmc.getMaster());
		if (mtmc.getSlave() != null) decorateJEEEUDomain(mtmc.getSlave());
		if (mtmc.getMasterId() != null) decorateJEEEUField(mtmc.getMasterId());
		if (mtmc.getSlaveId() != null) decorateJEEEUField(mtmc.getSlaveId());		
	}

	public static void decorateSimpleMtmCandidate(ManyToManyCandidate mtmc){
		if (mtmc.getMaster() != null) decorateSimpleDomain(mtmc.getMaster());
		if (mtmc.getSlave() != null) decorateSimpleDomain(mtmc.getSlave());
		if (mtmc.getMasterId() != null) decorateSimpleField(mtmc.getMasterId());
		if (mtmc.getSlaveId() != null) decorateSimpleField(mtmc.getSlaveId());		
	}
	
	public static void decorateJEEEUProject(Project project){
		for (Domain d: project.getDomains()) {
			decorateJEEEUDomain(d);
		}
		for(List<Domain> dds: project.getDataDomains()){
			for (Domain dd: dds) {
				decorateJEEEUDomain(dd);
			}
		}
		for (ManyToManyCandidate mtmc:project.getMtmCandidates()){
			decorateJEEEUMtmCandidate(mtmc);
		}
		for (List<ManyToManyCandidate> vmtmcs :project.getMtmCandidatesValues()) {
			for (ManyToManyCandidate vmtmc:vmtmcs){
				decorateJEEEUMtmCandidate(vmtmc);
			}
		}
	}

    public static void decorateSimpleProject(Project project) {
		for (Domain d: project.getDomains()) {
			decorateSimpleDomain(d);
		}
		for(List<Domain> dds: project.getDataDomains()){
			for (Domain dd: dds) {
				decorateSimpleDomain(dd);
			}
		}
		for (ManyToManyCandidate mtmc:project.getMtmCandidates()){
			decorateSimpleMtmCandidate(mtmc);
		}
		for (List<ManyToManyCandidate> vmtmcs :project.getMtmCandidatesValues()) {
			for (ManyToManyCandidate vmtmc:vmtmcs){
				decorateSimpleMtmCandidate(vmtmc);
			}
		}
	}

	public static List<List<ManyToMany>> getMtmValuesFromRawDomains(List<Domain> domains) throws Exception{
		List<ManyToMany> mtmsl = new ArrayList<>();
		List<String> removeNames = new ArrayList<>();
		List<List<ManyToMany>> result = new ArrayList<>();
		for (Domain rd:domains) {
			Map findResults = decideManyToMany(rd,domains);
			if (findResults != null && findResults.size()>0) {
				List<ManyToMany> mtms = (List<ManyToMany>) findResults.get("manyToManies");
				List<String> removeDomainNames = (List<String>) findResults.get("removeDomainNames");
				mtmsl.addAll(mtms);
				removeNames.addAll(removeDomainNames);
			}
		}
		for (int i=0; i< domains.size();i++) {
			if (removeNames.contains(domains.get(i).getStandardName())) {
				List<Domain> dataDomains = domains.get(i).getDummyDb();
				List<ManyToMany> mtms = new ArrayList<>();
				String removeName = domains.get(i).getStandardName();
				int rPos = removeNames.indexOf(removeName);
				for (Domain d: dataDomains) {
					ManyToMany mtm = (ManyToMany) mtmsl.get(rPos).deepClone();
					mtm.setMasterValue(d.findFieldByFieldName(mtm.getMaster().getDomainId().getFieldName()).getFieldValue());
					mtm.setValues(d.findFieldByFieldName(mtm.getSlave().getDomainId().getFieldName()).getFieldValue());
					mtms.add(mtm);
				}
				result.add(mtms);
			}
		}
		return result;
	}
	
	public static Domain attachMtmToDataDomain(Domain domain, Domain dataDomain, ManyToMany mtm, List<ManyToMany> vmtms) throws ValidateException{
		ManyToMany tmtm = mergeManyToManiesList(dataDomain,vmtms);
		tmtm.setStandardName(mtm.getStandardName());
		dataDomain = updateManyToMany(dataDomain, tmtm);
		return dataDomain;
	}
	
	public static Domain updateManyToMany(Domain domain,ManyToMany dmtm) {
		Set<ManyToMany> manyToManies = domain.getManyToManies();
		Set<ManyToMany> retVals = new TreeSet<>();
		for (ManyToMany mtm : manyToManies) {
			if (mtm.getMaster().getStandardName().equals(dmtm.getMaster().getStandardName())&&mtm.getSlave().getStandardName().equals(dmtm.getSlave().getStandardName())) {
				retVals.add(dmtm);
			}else {
				retVals.add(mtm);
			}
		}
		domain.setManyToManies(retVals);
		return domain;
	}
	
	public static Domain decorateEmptyMtmToDataDomain(Domain domain, Domain dataDomain) throws ValidateException{
		for (ManyToMany omtm:domain.getManyToManies()) {
			boolean mtmExists = false;
			for (ManyToMany imtm:dataDomain.getManyToManies()) {
				if (imtm.getMaster().getStandardName().equals(omtm.getMaster().getStandardName())&&imtm.getSlave().getStandardName().equals(omtm.getSlave().getStandardName())) {
					mtmExists = true;
				}
			}
			if (mtmExists == false) {
				ManyToMany empMtm = (ManyToMany)omtm.deepClone();
				empMtm.setMasterValue("");
				if (dataDomain.getDomainId().getFieldValue()!=null)empMtm.setMasterValue(dataDomain.getDomainId().getFieldValue());
				empMtm.setValues("");
				empMtm.setStandardName(omtm.getSlave().getStandardName());
				dataDomain.addManyToMany(empMtm);
			}
		}
		return dataDomain;
	}

	
	public static ManyToMany mergeManyToManiesSet(ManyToMany target,Set<ManyToMany> manyToManies) {
		for (ManyToMany mtm:manyToManies) {
			if (target.getMasterValue().equals(mtm.getMasterValue())) {
				target.setValues(mergeQuotedStrings(target.getValues(),mtm.getValues()));
			}
		}
		return target;
	}
	
	public static ManyToMany mergeManyToManiesList(Domain targetDomain,List<ManyToMany> manyToManies) {
		ManyToMany vMtm = (ManyToMany)manyToManies.get(0).deepClone();
		vMtm.setMasterValue(targetDomain.getDomainId().getFieldValue());
		vMtm.setValues("");
		for (ManyToMany mtm:manyToManies) {
			if (targetDomain.getDomainId().getFieldValue().equals(mtm.getMasterValue())) {
				vMtm.setValues(mergeQuotedStrings(vMtm.getValues(),mtm.getValues()));
			}
		}
		return vMtm;
	}
	
	public static String mergeQuotedStrings(String str1, String str2) {
		String [] strSet1 = str1.split(",");
		String [] strSet2 = str2.split(",");
		Set<String> intsSet = new TreeSet<>();
		for (String str : strSet1) {
			if (StringUtil.isInteger(str)) intsSet.add(str);
		}
		for (String str : strSet2) {
			if (StringUtil.isInteger(str)) intsSet.add(str);
		}
		String result = Strings.join(intsSet, ',');
		if (result.endsWith(",")) result = result.substring(0,result.length()-1);
		return result;		
	}
	
	public static boolean listContainsId(List<Domain> domains, String idValue) {
		ListIterator<Domain> dIter = domains.listIterator();
		while (dIter.hasNext()) {
			Domain d = dIter.next();
			if (d.getDomainId().getFieldValue().equals(idValue)) return true;
		}
		return false;
	}
}
